2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import <Adium/AIAccount.h>
18 #import <Adium/AIChat.h>
19 #import <Adium/AIContactControllerProtocol.h>
20 #import <Adium/AIChatControllerProtocol.h>
21 #import <Adium/AIService.h>
22 #import <Adium/AIListContact.h>
23 #import <Adium/AIMetaContact.h>
24 #import <Adium/DCJoinChatViewController.h>
25 #import <AIUtilities/AIStringAdditions.h>
27 @interface DCJoinChatViewController (PRIVATE)
28 - (NSString *)impliedCompletion:(NSString *)aString;
31 @implementation DCJoinChatViewController
33 //Create a new join chat view
34 + (DCJoinChatViewController *)joinChatView
36 return [[[self alloc] init] autorelease];
42 if ((self = [super init]))
47 NSString *nibName = [self nibName];
50 [NSBundle loadNibNamed:nibName owner:self];
59 [view release]; view = nil;
70 //Stubs for subclasses
71 - (NSString *)nibName {
74 - (void)joinChatWithAccount:(AIAccount *)inAccount
79 - (void)configureForAccount:(AIAccount *)inAccount
81 if (inAccount != account) {
83 account = [inAccount retain];
87 - (NSString *)impliedCompletion:(NSString *)aString
94 * @brief Join a group chat with given information
96 * @param inName The name of the chat
97 * @param inAccount The account on which to join
98 * @param inInfo Account-specific information which can be used by the account while joining or creating the chat
99 * @param contactsToInvite An array of AIListContacts which will be invited to the chat once Chat_DidOpen is posted for it (once it is open)
100 * @param invitationMessage A message sent to contactsToInvite. Ignored if contactsToInvite is nil.
102 - (void)doJoinChatWithName:(NSString *)inName
103 onAccount:(AIAccount *)inAccount
104 chatCreationInfo:(NSDictionary *)inInfo
105 invitingContacts:(NSArray *)contactsToInvite
106 withInvitationMessage:(NSString *)invitationMessage
109 AILog(@"Creating chatWithName:%@ onAccount:%@ chatCreationInfo:%@",inName,inAccount,inInfo);
112 chat = [[adium chatController] chatWithName:inName
115 chatCreationInfo:inInfo];
117 if ([contactsToInvite count]) {
118 [chat setStatusObject:contactsToInvite forKey:@"ContactsToInvite" notify:NotifyNever];
120 if ([invitationMessage length]) {
121 [chat setStatusObject:invitationMessage forKey:@"InitialInivitationMessage" notify:NotifyNever];
124 [[adium notificationCenter] addObserver:self selector:@selector(chatDidOpen:) name:Chat_DidOpen object:chat];
130 //When the chat opens, we are ready to send out our invitations to join it
131 - (void)chatDidOpen:(NSNotification *)notification
133 NSArray *contacts = [chat statusObjectForKey:@"ContactsToInvite"];
135 if (contacts && [contacts count]) {
136 NSMutableDictionary *inviteUsersDict;
137 NSString *initialInvitationMessage = [chat statusObjectForKey:@"InitialInivitationMessage"];
139 inviteUsersDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:[[contacts mutableCopy] autorelease],@"ContactsToInvite",nil];
140 if (initialInvitationMessage) {
141 [inviteUsersDict setObject:initialInvitationMessage
142 forKey:@"InitialInivitationMessage"];
144 AILog(@"scheduling invitation of %@",inviteUsersDict);
145 [NSTimer scheduledTimerWithTimeInterval:0.01
147 selector:@selector(inviteUsers:)
148 userInfo:inviteUsersDict
152 //The dictionary will retain the ContactsToInvite and InitialInivitationMessage objects;
153 //The timer will retain the dictionary until it is invalidated.
154 [chat setStatusObject:nil forKey:@"ContactsToInvite" notify:NotifyNever];
155 [chat setStatusObject:nil forKey:@"InitialInivitationMessage" notify:NotifyNever];
157 //We are no longer concerned with the opening of this chat.
158 [[adium notificationCenter] removeObserver:self name:Chat_DidOpen object:chat];
162 * @brief Timer method to invite contacts to a chat
164 * This is called repeatedly by the scheduled timer until all users have been invited to the chat.
165 * This is done incrementally to prevent beachballing if the process is slow and a large number of users are invited.
167 - (void)inviteUsers:(NSTimer *)inTimer
169 NSMutableDictionary *userInfo = [inTimer userInfo];
170 NSMutableArray *contactArray = [userInfo objectForKey:@"ContactsToInvite"];
172 if ([contactArray count]) {
173 AIListContact *listContact = [[contactArray objectAtIndex:0] retain];
174 [contactArray removeObjectAtIndex:0];
175 AILog(@"Inviting %@ to %@", listContact, chat);
177 [chat inviteListContact:listContact
178 withMessage:[userInfo objectForKey:@"InitialInivitationMessage"]];
179 [listContact release];
182 [inTimer invalidate];
187 * @brief Generate an array of AIListContacts given a string and an account
189 * @param namesSeparatedByCommas A string in the form @"Contact1,Another Contact,A Third Contact"
190 * @param inAccount The account on which the contacts should be created
192 - (NSArray *)contactsFromNamesSeparatedByCommas:(NSString *)namesSeparatedByCommas onAccount:(AIAccount *)inAccount;
194 NSMutableArray *contactsArray = nil;
195 NSArray *contactNames;
196 AILog(@"contactsFromNamesSeparatedByCommas:%@ onAccount:%@",namesSeparatedByCommas,inAccount);
197 if ([namesSeparatedByCommas length]) {
199 contactNames = [namesSeparatedByCommas componentsSeparatedByString:@","];
201 if ([contactNames count]) {
202 NSEnumerator *enumerator;
203 NSString *aContactName, *UID;
204 AIListContact *listContact;
206 contactsArray = [NSMutableArray array];
208 enumerator = [contactNames objectEnumerator];
209 while ((aContactName = [enumerator nextObject])) {
211 UID = [[inAccount service] filterUID:[self impliedCompletion:aContactName] removeIgnoredCharacters:YES];
213 //If the service is not case sensitive, compact the string before proceeding so our UID will be correct
214 if (![[inAccount service] caseSensitive]) {
215 UID = [UID compactedString];
218 if ((listContact = [[adium contactController] contactWithService:[inAccount service]
221 [contactsArray addObject:listContact];
227 AILog(@"contactsArray is %@",contactsArray);
228 return contactsArray;
231 #pragma mark Drag delegate convenience
234 * @brief Find an online contact with the specified service from a unique ID
236 * @result An online AIListContact on service, or nil.
238 - (AIListContact *)validContact:(NSString *)uniqueID withService:(AIService *)service
240 AIListContact *listContact = nil;
241 AIListObject *listObject = [[adium contactController] existingListObjectWithUniqueID:uniqueID];
244 if ( [listObject isKindOfClass:[AIMetaContact class]] ) {
245 listContact = [(AIMetaContact *)listObject preferredContactWithCompatibleService:service];
246 } else if ( [listObject isKindOfClass:[AIListContact class]] ) {
247 if ([[listObject service] isEqualTo:service]) {
248 listContact = (AIListContact *)listObject;
252 if ( listContact && [listContact online] ) {
260 // Tests if dragged objects are valid for this account
261 // Must be called by explicitly the subclass
262 - (NSDragOperation)doDraggingEntered:(id <NSDraggingInfo>)sender
264 // Test whether this drag item is acceptable
265 NSPasteboard *pboard = [sender draggingPasteboard];
267 // Are there list objects being dragged?
268 if ([pboard availableTypeFromArray:[NSArray arrayWithObject:@"AIListObject"]]) {
270 // If so, get the ID's
271 if ([[pboard availableTypeFromArray:[NSArray arrayWithObject:@"AIListObjectUniqueIDs"]] isEqualToString:@"AIListObjectUniqueIDs"]) {
272 NSArray *dragItemsUniqueIDs;
274 NSEnumerator *enumerator;
276 dragItemsUniqueIDs = [pboard propertyListForType:@"AIListObjectUniqueIDs"];
278 enumerator = [dragItemsUniqueIDs objectEnumerator];
279 while ((uniqueID = [enumerator nextObject])) {
281 // Is there a contact with our service?
282 if ( [self validContact:uniqueID withService:[account service]] ) {
284 //if ([[view window] firstResponder] != textField_inviteUsers)
285 // [[view window] makeFirstResponder:textField_inviteUsers];
286 return NSDragOperationGeneric;
292 //if we reach this point, no valid contacts were dragged
293 return NSDragOperationNone;
296 // Accepts list contacts being dragged over theField and adds their ID's to the field in a nice manner
297 // Note: subclasses must call this explicitly
298 - (BOOL)doPerformDragOperation:(id <NSDraggingInfo>)sender toField:(NSTextField *)theField
300 NSPasteboard *pboard = [sender draggingPasteboard];
302 // Were ListObjects dragged?
303 if ([pboard availableTypeFromArray:[NSArray arrayWithObject:@"AIListObject"]]) {
305 // If so, get the unique ID's
306 if ([[pboard availableTypeFromArray:[NSArray arrayWithObject:@"AIListObjectUniqueIDs"]] isEqualToString:@"AIListObjectUniqueIDs"]) {
307 NSArray *dragItemsUniqueIDs;
309 AIListObject *listObject;
310 AIListContact *listContact;
311 NSEnumerator *enumerator;
313 dragItemsUniqueIDs = [pboard propertyListForType:@"AIListObjectUniqueIDs"];
315 enumerator = [dragItemsUniqueIDs objectEnumerator];
316 while ((uniqueID = [enumerator nextObject])) {
317 NSString *oldValue = [theField stringValue];
318 listObject = [[adium contactController] existingListObjectWithUniqueID:uniqueID];
320 // Get contacts with our service
321 // (May not be necessary, as we reject ungood contacts in the dragging entered phase)
322 if ((listContact = [self validContact:uniqueID withService:[account service]])) {
324 // Add a comma for prettiness if need be
325 if ( [oldValue length] && ![[oldValue substringFromIndex:([oldValue length]-1)] isEqualToString:@","] ) {
326 oldValue = [oldValue stringByAppendingString:@", "];
327 [theField setStringValue:oldValue];
329 [theField setStringValue:[oldValue stringByAppendingString:[listContact displayName]]];
337 #pragma mark Delegate handling
338 - (void)setDelegate:(id)inDelegate
340 delegate = inDelegate;
347 #pragma mark Roomlist Delegate
348 - (void)setSharedChatInstance:(id)newInstance
350 NSLog(@"sharedChatInstance: %@",newInstance);
351 sharedChatInstance = newInstance;
354 -(id)sharedChatInstance
356 return sharedChatInstance;